﻿// Copyright (c) 2012 - 2020 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2018-12-05 version 3.1.5 Jared Zheng <jaredz at outlook dot com>

#include "stdafx.h"
#include "CommonServer.h"
#include "commonpacket.h"
#include "RoutineImp.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// CCenterRoutine
IMPLEMENT_RTTI_CREATE( CCenterRoutine, CObject, CObjectTraits::OBJECTT_NONE )

UInt CCenterRoutine::OnHandle(Int nEvent, uintptr_t utData, ULLong ullParam)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->OnHandle(nEvent, utData, ullParam);
    }
    return RET_OKAY;
}

UInt CCenterRoutine::OnHandle(Int nEvent, CEventBase& EventRef, ULLong ullParam)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->OnHandle(nEvent, EventRef, ullParam);
    }
    return RET_OKAY;
}

UInt CCenterRoutine::OnHandle(Int nEvent, CStream& Stream, ULLong ullParam)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->OnHandle(nEvent, Stream, ullParam);
    }
    return RET_OKAY;
}

UInt CCenterRoutine::OnHandle(Int, UInt)
{
    if (m_pServer->GetServerInfo(PAKT_CENTER)->IsMayDay() == false)
    {
        // 检测更新线程定时器是否工作正常
        if (CEventQueue::CheckTickEvent(CServerConfig::CFG_TIME_UPDATE_ID, DATAD_TIME))
        {
            m_pServer->GetServerInfo(PAKT_CENTER)->MayDay();
            DEV_ERROR(TF("[同步管理例程]更新线程异常, 进入异常状态"));
            LOG_ERROR(m_pServer->GetFileLog(), TF("[同步管理]更新线程异常, 进入异常状态"));
        }
    }
    if (m_pServer->GetServerInfo(PAKT_CENTER)->IsMayDay()) // network & tick thread exception
    {
        DEV_ERROR(TF("[同步管理例程]异常状态-----上报状态到redis由web端呈现？？？发送短信通知"));
    }
    if (m_pServer->GetServerInfo(PAKT_CENTER)->IsWarnLoad()) // listen tcp close 或者其他
    {
        DEV_WARN(TF("[同步管理例程]告警状态-----上报状态到redis由web端呈现？？？发送短信通知"));
    }
    return RET_OKAY;
}

bool CCenterRoutine::Start(ICommonServer* pServer)
{
    m_pServer = pServer;
    if ((m_pServer != nullptr) && LoadExtConfig())
    {
        // 同步管理的例程是否开启异步事件队列是可选操作
        m_uThreads = (UInt)m_pServer->GetConfig().GetServerValue(CServerConfig::CFG_LOAD_CENTER);
        if (m_uThreads > 0)
        {
            m_uThreads = CPlatform::CheckThread(m_uThreads);
            LOGV_INFO(m_pServer->GetFileLog(), TF("[同步管理例程]异步事件队列处理线程=%d, %s"), m_uThreads, (m_uThreads > 1) ? TF("启用多线程并发处理") : TF("启用单线程处理"));
        }
        m_nUpdate  = 0;
        // 超时为更新周期的倍数
        SetTimeout();
        // 开启检测更新线程异常的线程
        CEventQueue::CreateTickEvent(UPDATE_TIME_ID, m_pServer->GetConfig().GetUpdateTick() * TIMET_S2MS, *this);

        if (m_BusinessPtr != nullptr)
        {
            if (m_BusinessPtr->Start(pServer) == false) // 注意异步事件队列还未初始化
            {
                return false;
            }
        }
        if ((m_uThreads > 0) && CEventQueue::EventQueue(m_EventQueuePtr, *this, 0, (Int)m_uThreads))
        {
            m_EventQueuePtr->SetThreadEvent(PAKE_THREAD_START, PAKE_THREAD_STOP);
            return m_EventQueuePtr->Init();
        }
        return true;
    }
    return false;
}

bool CCenterRoutine::Pause(bool bPause)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->Pause(bPause);
    }
    return false;
}

bool CCenterRoutine::Update(void)
{
    if ((m_uThreads > 0) && (m_pServer->GetServerInfo(PAKT_CENTER)->IsMayDay() == false))
    {
        if (m_EventQueuePtr->CheckThreads(m_uTimeout))
        {
            m_pServer->GetServerInfo(PAKT_CENTER)->MayDay();
            DEV_ERROR(TF("[同步管理例程]事件队列线程异常, 进入异常状态"));
            LOG_ERROR(m_pServer->GetFileLog(), TF("[同步管理例程]事件队列线程异常, 进入异常状态"));
        }
    }
    if (CEventQueue::CheckTickEvent(UPDATE_TIME_ID, DATAD_TIME))
    {
        DEV_WARN(TF("[同步管理例程]超时检测线程异常, 重启定时器"));
        LOG_WARN(m_pServer->GetFileLog(), TF("[同步管理例程]超时检测线程异常, 重启定时器"));
        CEventQueue::DestroyTickEvent(UPDATE_TIME_ID);
        CEventQueue::CreateTickEvent(UPDATE_TIME_ID, m_pServer->GetConfig().GetUpdateTick() * TIMET_S2MS, *this);
    }
    // 定时更新线程由检测线程判断

    // 定时检测扩展配置文件是否有修改
    ++m_nUpdate;
    if (m_nUpdate >= m_nTime)
    {
        m_nUpdate = 0;
        LoadExtConfig(true);
    }
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->Update();
    }
    return true;
}

void CCenterRoutine::Stop(void)
{
    CEventQueue::DestroyTickEvent(UPDATE_TIME_ID);
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->Stop();
        CString strName = m_BusinessPtr->GetRTTI().GetName();
        m_BusinessPtr->Exit();
        m_BusinessPtr = nullptr;
        CObjectTraits::Unload<ICommonRoutine>(*strName);
    }
    if (m_EventQueuePtr != nullptr)
    {
        m_EventQueuePtr->Exit();
        m_EventQueuePtr = nullptr;
    }
    m_pServer      = nullptr;
    m_nUpdate      = 0;
    m_uThreads     = 0;
    m_uTimeout     = 0;
}

bool CCenterRoutine::SetTimeout(UInt uTimeout)
{
    if (uTimeout == 0)
    {
        m_uTimeout = m_pServer->GetConfig().GetUpdateTick() * DATAD_TIME;
    }
    else
    {
        m_uTimeout = uTimeout;
        m_uTimeout = DEF::Max<UInt>(1, m_uTimeout);
    }
    return true;
}

bool CCenterRoutine::AddQueue(Int nEvent, uintptr_t utData, ULLong ullParam, QUEUE_TYPE eType)
{
    if (m_pServer == nullptr)
    {
        return false;
    }
    // 入异步事件队列前的检测尽量不要处理复杂功能, 需要处理的建议进队列回调处理
    // 有加锁的建议进队列回调处理
    // "ServerInfo"
    // PAKE_UNLINK, SERVER_UNLINK(type, id), socket
    bool bRet = false;
    if (m_BusinessPtr != nullptr)
    {
        bRet = m_BusinessPtr->AddQueue(nEvent, utData, ullParam, eType);
    }
    switch (nEvent)
    {
    case PAKE_LINKACK:    // 向管理服务器注册回复结果
    case PAKE_UNLINK:     // 向本服务器注册的注销/断开
    case PAKE_UNLINKACK:  // 向管理服务器注销/断开回复结果
    case PAKE_TCP_CLOSED: // 连接的外网端注销/断开
        {
            bRet = true;
        }
        break;
    default: {}
    }
    if (bRet && (m_uThreads > 0))
    {
        bRet = m_EventQueuePtr->Add(nEvent, utData, ullParam, eType);
    }
    return bRet;
}

bool CCenterRoutine::AddQueue(Int nEvent, CEventBase& EventRef, ULLong ullParam, QUEUE_TYPE eType)
{
    if (m_pServer == nullptr)
    {
        return false;
    }
    // 入异步事件队列前的检测尽量不要处理复杂功能, 需要处理的建议进队列回调处理
    // 有加锁的建议进队列回调处理
    // "ServerInfo"
    // PAKE_LINK/PAKE_UPDATE, EventRef, PTCP_PARAM

    // "CommonEvent"
    // [PAKE_COMMON_BEGIN, PAKE_COMMON_END], EventRef, PTCP_PARAM
    bool bRet = false;
    if (m_BusinessPtr != nullptr)
    {
        bRet = m_BusinessPtr->AddQueue(nEvent, EventRef, ullParam, eType);
    }
    ULLong ullSocket = ullParam;
    if (eType > QUEUE_TYPE_MASK)
    {
        CNETTraits::PTCP_PARAM pTcp = reinterpret_cast<CNETTraits::PTCP_PARAM>(ullParam);
        ullSocket = pTcp->sSocket;
    }
    if (bRet && (m_uThreads > 0))
    {
        bRet = m_EventQueuePtr->Add(nEvent, EventRef, ullSocket, (eType & QUEUE_TYPE_MASK));
    }
    return bRet;
}

bool CCenterRoutine::AddQueue(Int nEvent, CStream& Stream, ULLong ullParam)
{
    if (m_pServer == nullptr)
    {
        return false;
    }
    // 入异步事件队列前的检测尽量不要处理复杂功能, 需要处理的建议进队列回调处理
    // 有加锁的建议进队列回调处理
    bool bRet = false;
    if (m_BusinessPtr != nullptr)
    {
        bRet = m_BusinessPtr->AddQueue(nEvent, Stream, ullParam);
    }
    if (bRet && (m_uThreads > 0))
    {
        bRet = m_EventQueuePtr->Add(nEvent, Stream, ullParam);
    }
    return bRet;
}

bool CCenterRoutine::LoadExtConfig(bool bUpdate)
{
    CStringFix strPath;
    m_pServer->GetConfig().GetServerExtConfig(CServerConfig::CFG_LOAD_CENTER, strPath);

    CFileAttr fa;
    m_pServer->GetConfig().GetFilePath().FileAttr(fa, *strPath);
    if (bUpdate)
    {
        if (fa.MTime(m_tExtConfig))
        {
            return true;
        }
        LOGV_INFO(m_pServer->GetFileLog(), TF("[同步管理例程]定时读取文件修改时间%llX---%llX"), fa.MTime(), m_tExtConfig);
        m_tExtConfig = fa.MTime();
    }
    else
    {
        m_tExtConfig = fa.MTime();
        LOGV_INFO(m_pServer->GetFileLog(), TF("[同步管理例程]初始化读取文件修改时间=%llX"), m_tExtConfig);
    }
    LOGV_INFO(m_pServer->GetFileLog(), TF("[同步管理例程]ExtConfig=%s"), *strPath);

    CFileReadStream Reader;
    if (Reader.Create(*strPath))
    {
        CKVNode  KVNode;
        CKVStore KVStore;
        if (KVStore.Load(KVNode, Reader))
        {
            if (KVNode.Find(TF("Time")))
            {
                m_nTime = KVNode[TF("Time")].GetInt();
            }

            bool bRet = true;
            if ((bUpdate == false) && (KVNode.Find(TF("BusinessRoutine"))))
            {
                bRet = LoadExtBusiness(KVNode);
            }
            if (m_BusinessPtr != nullptr)
            {
                bRet = (m_BusinessPtr->OnHandle(bUpdate ? PAKE_CONFIG_UPDATE : PAKE_CONFIG_LOAD,
                                                reinterpret_cast<uintptr_t>(&KVNode),
                                                (ULLong)m_pServer) == RET_OKAY);
            }
            return bRet;
        }
        else
        {
            LOGV_WARN(m_pServer->GetFileLog(), TF("[同步管理例程]解析ExtConfig=%s文件内容失败"), *strPath);
        }
    }
    else
    {
        LOGV_WARN(m_pServer->GetFileLog(), TF("[同步管理例程]打开ExtConfig=%s文件不存在"), *strPath);
    }
    return true;
}

bool CCenterRoutine::LoadExtBusiness(CKVNode& Root)
{
    CKVString strName;
    CKVString strObject;
    CKVString strModule;

    CKVNode& Routine = Root[TF("BusinessRoutine")];
    Routine[TF("Name")].GetString(strName);
    Routine[TF("Object")].GetString(strObject);
    Routine[TF("Module")].GetString(strModule);
#if (__PLATFORM_TARGET__ == PLATFORM_TARGET_LINUX)
    strModule.Insert(0, CServerConfig::LibPrefix);
#endif
    strModule += CServerConfig::LibExt;

    assert(m_BusinessPtr == nullptr);
    if (CObjectTraits::Load<ICommonRoutine>(*strName, *strModule, m_BusinessPtr, *strObject))
    {
        LOGV_INFO(m_pServer->GetFileLog(), TF("[同步管理例程]RTTI load --- Object=%s, Name=%s, Module=%s --- %p"), *strObject, *strName, *strModule, m_BusinessPtr.Get());
        if (m_BusinessPtr->Init() == (UInt)RET_OKAY)
        {
            m_BusinessPtr->OnHandle(PAKE_CONFIG_DEFAULT, reinterpret_cast<uintptr_t>(this), (ULLong)m_pServer);
            return true;
        }
    }
    LOGV_ERROR(m_pServer->GetFileLog(), TF("[同步管理例程]RTTI load --- Object=%s, Name=%s, Module=%s --- Failed!"), *strObject, *strName, *strModule);
    return false;
}

#if (__PLATFORM_TARGET__ == PLATFORM_TARGET_WINDOWS)
// Windows Realse版本MSVC编译器如果不强制调用静态常量, 静态常量会被优化掉
BOOL WINAPI DllMain(HMODULE hModule, ULong ulReason, void* lpReserved)
{
    UNREFERENCED_PARAMETER( lpReserved );

    if (ulReason == DLL_PROCESS_ATTACH)
    {
        ::DisableThreadLibraryCalls(hModule);
        DEV_DUMP(TF("$ Link %s.dll --- DLL_PROCESS_ATTACH"), CCenterRoutine::ms_RTTI.GetName());
    }
    else if (ulReason == DLL_PROCESS_DETACH)
    {
        DEV_DUMP(TF("$ Unlink %s.dll --- DLL_PROCESS_DETACH"), CCenterRoutine::ms_RTTI.GetName());
    }
    return TRUE;
}
#endif
